package com.glodon.paas.databag.resource;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.glodon.paas.databag.api.bean.DatabagDto;
import com.glodon.paas.databag.api.bean.ResourceDto;
import com.glodon.paas.databag.api.bean.TaskDto;
import com.glodon.paas.databag.service.DatabagService;
import com.glodon.paas.databag.service.TaskService;
import com.glodon.paas.exception.PaasServerExceptionFactory;
import com.glodon.paas.job.JobConstants;
import com.glodon.paas.job.model.TaskStatus;
import com.glodon.paas.job.util.JsonUtil;

/**
 * 处理所有文件相关的资源.
 * 
 * @author botao.liao
 * 
 */
@Component
@Path("/internal")
public class DatabagResource {
    private final Logger logger = LoggerFactory.getLogger(DatabagResource.class);
    private final ExecutorService pool = Executors.newFixedThreadPool(5);
    private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    @Value("${databag.thumbnail.defaultsize:64}")
    private final String DEFAULT_THUMBNAIL_SIZE = "64";
    @Autowired
    private DatabagService databagService;
    @Autowired
    private TaskService taskService;

    @POST
    @Path("/{type}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response create(@Context HttpServletRequest request, @PathParam("type") String type, InputStream is)
            throws IOException {
        String body = IOUtils.toString(is);
        body = URLDecoder.decode(body, "utf-8");
        logger.debug("creating databag: {}", body);
        DatabagDto dto = null;
        try {
            dto = JsonUtil.jsonToObject(body, DatabagDto.class);
        } catch (Exception e) {
            logger.error("json unmarshall failed.", e);
            throw PaasServerExceptionFactory.validateException("invalid post body:" + body, e);
        }
        dto.setType(type);
        DatabagDto databag = databagService.create(dto);

        Map<String, String> result = new HashMap<String, String>();
        result.put("databagId", databag.getId());
        return Response.ok(result, MediaType.APPLICATION_JSON).build();
    }

    @GET
    @Path("/{databagId}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response queryById(@PathParam("databagId") String databagId) {
        DatabagDto dto = databagService.get(databagId);
        // String result = convert2Json(dto);
        return Response.ok(dto, MediaType.APPLICATION_JSON).build();
    }

    @GET
    @Path("/seed/{source}/{localId}/{version}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response queryByMeta(@PathParam("source") String source, @PathParam("localId") String localId,
            @PathParam("version") String version) {
        List<DatabagDto> bags = databagService.findByMetadata(source, localId, version);
        List<String> result = new ArrayList<String>();
        for (DatabagDto databagDto : bags) {
            result.add(convert2Json(databagDto));
        }
        return Response.ok(result, MediaType.APPLICATION_JSON).build();
    }

    /**
     * 获取创建数据包时的原始文件.
     * 
     * @param databagId
     *            数据包Id
     * @param download
     *            输出流中是否在浏览器中表现为下载
     * @return
     */
    @GET
    @Path("/{databagId}/resource")
    public Response getResource(@PathParam("databagId") String databagId, @QueryParam("download") boolean download) {
        ResourceDto resource = databagService.getResource(databagId);
        ResponseBuilder builder = Response.ok(resource.getInputStream(), resource.getMime());
        builder.header("Content-Length", resource.getLength());
        if (download == true) {
            String name = resource.getName();
            builder.header("Content-Disposition", "attachment; filename=\"" + name + "\"");
        }
        Response response = builder.cacheControl(getCacheControl()).build();
        return response;
    }

    private String convert2Json(DatabagDto dto) {
        Map<String, String> result = new HashMap<String, String>();
        result.put(JobConstants.JOB_ID, dto.getId());
        result.put(JobConstants.JOB_START_TIME, dateFormat.format(dto.getStartTime()));
        if (dto.getEndTime() == null) {// 状态未知
            result.put(JobConstants.TASK_PROGRESS, "processing");
        } else {
            result.put(JobConstants.TASK_PROGRESS, JobConstants.JOB_COMPLETE);
            result.put(JobConstants.JOB_END_TIME, dateFormat.format(dto.getEndTime()));
            result.put(JobConstants.OUTPUT, dto.getOutput());
        }
        return JsonUtil.objectToJson(result);
    }

    @GET
    @Path("/{databagId}/thumbnail")
    public Response getThumbnail(@PathParam("databagId") String databagId) {
        return getThumbnail(databagId, DEFAULT_THUMBNAIL_SIZE);
    }

    @GET
    @Path("/{databagId}/thumbnail/{size}")
    public Response getThumbnail(@PathParam("databagId") String databagId, @PathParam("size") String size) {
        logger.info("request thumbnail for databagId:{}", databagId);
        ResourceDto thumbnail = databagService.getThumbnail(databagId, size);
        ResponseBuilder builder = Response.ok(thumbnail.getInputStream(), thumbnail.getMime());
        builder.cacheControl(getCacheControl());
        Response response = builder.build();
        return response;
    }

    @GET
    @Path("/{databagId}/mime/{mime}/**")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getResourceByMime(@PathParam("databagId") String databagId, @PathParam("mime") String mime) {
        logger.info("request mime \"{}\" for databagId:{}", mime, databagId);
        List<String> resourceByMime = databagService.getResourceByMime(databagId, mime);
        ResponseBuilder builder = Response.ok(resourceByMime, MediaType.APPLICATION_JSON);
        builder.cacheControl(getCacheControl());
        return builder.build();
    }

    @GET
    @Path("/{databagId}/path/{fileName:.+}")
    public Response getResourceByPath(@PathParam("databagId") String databagId, @PathParam("fileName") String fileName,
            @QueryParam("download") boolean download) {
        logger.info("request resource \"{}\" for databagId:{}", fileName, databagId);
        ResourceDto result = databagService.getResourceByPath(databagId, fileName);
        ResponseBuilder builder = Response.ok(result.getInputStream(), result.getMime());
        builder.header("Content-Length", result.getLength());
        if (download == true) {
            String name = fileName;
            int nameIndex = fileName.lastIndexOf("/");
            if (nameIndex > 0) {
                name = fileName.substring(nameIndex + 1);
            }
            builder.header("Content-Disposition", "attachment; filename=\"" + name + "\"");
        }
        builder.cacheControl(getCacheControl());
        Response response = builder.build();
        return response;
    }

    @GET
    @Path("/{databagId}/progress")
    public Response queryProgress(@PathParam("databagId") String databagId) {
        TaskDto taskDto = taskService.getTaskByDatabagId(databagId);
        TaskStatus status = taskDto.getStatus();
        String result = null;
        if (TaskStatus.running.equals(status) || TaskStatus.created.equals(status) || TaskStatus.success.equals(status)) {
            result = taskDto.getProgress() == null ? "0" : taskDto.getProgress().toString();
        } else {// 任务失败
            result = "-1"; // 约定-1代表任务失败
        }
        ResponseBuilder builder = Response.ok(result, MediaType.APPLICATION_JSON);
        if (TaskStatus.success.equals(status)) {
            builder.cacheControl(getCacheControl());
        }
        return builder.build();
    }

    @DELETE
    @Path("/{databagId}")
    public Response deleteResource(@PathParam("databagId") final String databagId,
            @QueryParam("alternatePublic") final String alternatePublic) {
        logger.info("delete databag for id:{}", databagId);
        pool.execute(new Runnable() {
            @Override
            public void run() {
                databagService.deleteResource(databagId, alternatePublic);
            }
        });
        return Response.ok().build();
    }

    private CacheControl getCacheControl() {
        CacheControl cc = new CacheControl();
        cc.setNoTransform(true);
        cc.setMustRevalidate(false);
        cc.setNoCache(false);
        cc.setMaxAge(Integer.MAX_VALUE);
        return cc;
    }
}
